package Compilador2014;

import java.awt.List;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.PushbackReader;
import java.util.LinkedList;
import java.util.Scanner;

import Compilador2014.ConjuntoErrores.TipoError;

public class AnalizadorLexico {

	private PushbackReader f;
	private int linea;
	private int columna;
	private int estado;
	private ConjuntoErrores errores;
	private LinkedList<Token> lista;
	
	public AnalizadorLexico (String nombre) {
		try {
			f = new PushbackReader (new FileReader (new File (nombre)));
			linea = 1;
			columna = 0;
			estado = 0;
			errores = new ConjuntoErrores ();
			lista = new LinkedList<Token> ();
			leerArchivo ();
			f.close();
		}
		catch (Exception e) {
			f = null;
			System.err.println("Error de fichero");
		}
	}
	
	public ConjuntoErrores getErrores () {
		return errores;
	}
	
	public LinkedList<Token> getLista() {
		return lista;
	}
	
	@SuppressWarnings("unused")
	private char leerCaracter(){
		try {
			int n;
			char c;
			
			n = f.read();
			if(n == -1)
				c = '$';
			else
				c = (char) n;
			columna++;
			System.out.println("fila: "+linea+" columna: "+columna+" he leido: "+c);
			return c;
		} catch (Exception e) {
			System.err.println("Error - Excepcion E/S en buffer de lectura.");
			return '#';
		}
	}
	public void leerArchivo () {
		try {
			String cad="";
			Token token;
			char c=' ';
			Error error;
			while (true) {
				switch (estado) {
				case 0:
					c = leerCaracter();
					if (c == '$')
						return;
					if (Character.isSpaceChar(c)) {
						if (c == '\r') { //Character.LINE_SEPARATOR) {
							linea++;
							columna = 0;
							estado = 0;
						}
						continue;
					}
					if (c == '\n') { //Character.LINE_SEPARATOR) {
						linea++;
						columna = 0;
						estado = 0;
						continue;
					}
					if (c == '\t') {
						columna = columna + 3;
						continue;
					}
					if (Character.isDigit(c)) {
						estado = 1;
						cad = "" + c;
					}
					else {
						if (Character.isLetter(c)) {
							estado = 5;
							cad = "" + c;
						}
						else {
							switch (c) {
							case ':':
								cad = "" + c;
								estado = 10;
								break;
							case '"':
								cad = "" + c;
								estado = 12;
								break;
							case ';':
							case ',':
							case '(':
							case ')':
							case '{':
							case '}':
								cad = "" + c;
								estado = 14;
								break;
							case '=':
								cad = "" + c;
								estado = 18;
								break;
							case '>':
								cad = "" + c;
								estado = 21;
								break;
							case '<':
								cad = "" + c;
								estado = 24;
								break;
							case '+':
								cad = "" + c;
								estado = 27;
								break;
							case '-':
							case '*':
							case '/':
								cad = "" + c;
								estado = 30;
								break;
							case '!':
								cad = "" + c;
								estado = 33;
								break;
							}							
						}
					}
					break;
				case 1:
					c = leerCaracter();
					if (c == '#' || c=='$') {
						token = new Token (cad, "num", linea, columna-cad.length());
						lista.add(token);
						return;
					}
					if (Character.isDigit(c))
						cad = cad + c;
					else {
						if (c == ',') {
							estado = 2;
							cad = cad + ".";
						}
						else {
							estado = 4;							
						}
					}
					break;
				case 2:
					c = leerCaracter();
					if (c == '#' && c=='$') {
						token = new Token (cad, "num", linea, columna-cad.length());
						lista.add(token);
						return;
					}
					if (Character.isDigit(c))
						cad = cad + c;
					else 
						estado = 3;
					break;
				case 3:
				case 4:
					//tengo un numero entero o real
					token = new Token (cad, "num", linea, columna-cad.length());
					lista.add(token);
					f.unread(c);
					columna--;
					estado = 0;
					break;
				case 5:
					c = leerCaracter ();
					if (c == '#' || c=='$') {
						if (cad.equals("int") || cad.equals("string") || cad.equals("bool") || cad.equals("real") || cad.equals("if") 
								|| cad.equals("else") || cad.equals("while") || cad.equals("printf") || cad.equals("scanf") || 
								cad.equals("true") || cad.equals("false") ) {
							token = new Token (cad, cad, linea, columna-cad.length());
							//guardar palabra reservada
							lista.add(token);
						}
						else {
							if (cad.equals("mod") || cad.equals("div")) {
								token = new Token (cad, "oper_arit", linea, columna-cad.length());
								//guardar operadores aritmeticos
								lista.add(token);
							}
							else {
								token = new Token (cad, "id", linea, columna-cad.length());
								//guardar identificador
								lista.add(token);
							}
						}
						return;
					}
					if (Character.isLetter(c)) 
						cad = cad + c;
					else  {
						if (c == '_') {
							cad = cad + c;
							estado = 7;
						}
						else
							estado = 6;
					}
					break;
				case 6:
					if (cad.equals("int") || cad.equals("string") || cad.equals("bool") || cad.equals("real") || cad.equals("if") 
							|| cad.equals("else") || cad.equals("while") || cad.equals("printf") || cad.equals("scanf") || 
							cad.equals("true") || cad.equals("false") ) {
						token = new Token (cad, cad, linea, columna-cad.length());
						//guardar palabra reservada
						lista.add(token);
					}
					else {
						if (cad.equals("mod") || cad.equals("div")) {
							token = new Token (cad, "oper_arit", linea, columna-cad.length());
							//guardar operadores aritmeticos
							lista.add(token);
						}
						else {
							token = new Token (cad, "id", linea, columna-cad.length());
							//guardar identificador
							lista.add(token);
						}
					}
					f.unread(c);
					columna--;
					estado = 0;
					break;
				case 7:
					c = leerCaracter ();
					if (c == '#' || c=='$') {
						if (cad.equals("Inicio_programa")) {
							token = new Token (cad, "Inicio_programa", linea, columna-cad.length());
							//guardar inicio programa
							lista.add(token);
						}
						else {
							//error, palabra no correcta
							error = new Error (linea, columna-cad.length(), cad+" Palabra no encontrada");
							errores.insertarError(error, TipoError.LEXICO);
						}
						return;
					}
					if (Character.isLetter(c))
						cad = cad + c;
					else {
						if (c == '.') {
							cad = cad + c;
							estado = 8;
						}
						else
							estado = 9;
					}
					break;
				case 8:
					if (cad.equals("fin_programa.")) {
						token = new Token (cad, "fin_programa.", linea, columna-cad.length());
						//he llegado al final del programa
						//rompo el while ?? o compruebo si hay mas cosas y es error??
						lista.add(token);
						//voy a seguir hasta que sea fin del programa
						//luego dare errores sintacticos
						estado = 0;
					}
					else {
						//error, palabra no correcta
						error = new Error (linea, columna-cad.length(), cad+" Palabra no encontrada");
						errores.insertarError(error, TipoError.LEXICO);
					}
					break;
				case 9:
					if (cad.equals("Inicio_programa")) {
						token = new Token (cad, "Inicio_programa", linea, columna-cad.length());
						//guardar inicio programa
						lista.add(token);
					}
					else {
						//error, palabra no correcta
						error = new Error (linea, columna-cad.length(), cad+" Palabra no encontrada");
						errores.insertarError(error, TipoError.LEXICO);
					}
					f.unread(c);
					columna--;
					estado = 0;
					break;
				case 10:
					c = leerCaracter ();
					if (c == '#' || c=='$') {
						//error, tengo dos puntos solos
						error = new Error (linea, columna, "Operador : no existe");
						errores.insertarError(error, ConjuntoErrores.TipoError.LEXICO);
						return;
					}
					if (c == '=') {
						cad = cad + c;
						estado = 11;
					}
					else
						estado = 37;
					break;
				case 11:
					token = new Token (cad, ":=", linea, columna-cad.length());
					//guardo operador de asignacion
					lista.add(token);
					estado = 0;
					break;
				case 12:
					c = leerCaracter ();
					if (c == '#' || c=='$') {
						//error, tengo dos puntos solos
						error = new Error (linea, columna, "Error literal sin cierre");
						errores.insertarError(error, ConjuntoErrores.TipoError.LEXICO);
						return;
					}
					cad = cad + c;
					if (c == '"') 
						estado = 13;
					break;
				case 13:
					token = new Token (cad, "literal", linea, columna-cad.length());
					//tengo que guardar el literal
					lista.add(token);
					estado = 0;
					break;
				case 14:
					token = new Token (cad, cad, linea, columna-1);
					//guardo punto y coma, coma, parentesis y llaves
					lista.add(token);
					estado = 0;
					break;
				case 18:
					c = leerCaracter ();
					if (c == '#' || c=='$') {
						token = new Token (cad, "=", linea, columna-1);
						//guardo igual
						lista.add(token);
						return;
					}
					if (c == '=') {
						cad = cad + c;
						estado = 20;
					}
					else {
						estado = 19;
					}
					break;
				case 19:
					token = new Token (cad, "=", linea, columna-1);
					//guardo igual
					lista.add(token);
					f.unread(c);
					columna--;
					estado = 0;
					break;
				case 20:
					token = new Token (cad, "oper_logico", linea, columna-cad.length());
					//guardo igual igual
					lista.add(token);
					estado = 0;
					break;
				case 21:
					c = leerCaracter ();
					if (c == '#' || c=='$') {
						token = new Token (cad, "oper_logico", linea, columna-1);
						//guardo mayor
						lista.add(token);
						return;						
					}
					if (c == '=') {
						cad = cad + c;
						estado = 23;
					}
					else {
						estado = 22;
					}
					break;
				case 22:
					token = new Token (cad, "oper_logico", linea, columna-1);
					//guardo mayor
					lista.add(token);
					f.unread(c);
					columna--;
					estado = 0;
					break;
				case 23:
					token = new Token (cad, "oper_logico", linea, columna-cad.length());
					//guardo mayor o igual
					lista.add(token);
					estado = 0;
					break;
				case 24:
					c = leerCaracter ();
					if (c == '#' || c=='$') {
						token = new Token (cad, "oper_logico", linea, columna-1);
						//guardo menor
						lista.add(token);
						return;
					}
					if (c == '=') {
						cad = cad + c;
						estado = 26;
					}
					else {
						estado = 25;
					}
					break;
				case 25:
					token = new Token (cad, "oper_logico", linea, columna-1);
					//guardo menor
					lista.add(token);
					f.unread(c);
					columna--;
					estado = 0;
					break;
				case 26:
					token = new Token (cad, "<=", linea, columna-cad.length());
					//guardo menor o igual
					lista.add(token);
					estado = 0;
					break;
				case 27:
					c = leerCaracter ();
					if (c == '#' || c=='$') {
						token = new Token (cad, "oper_arit", linea, columna-1);
						//guardo mas
						lista.add(token);
						return;
					}
					if (c == '+') {
						cad = cad + c;
						estado = 29;
					}
					else {
						estado = 28;
					}
					break;
				case 28:
					token = new Token (cad, "oper_arit", linea, columna-1);
					//guardo mas
					lista.add(token);
					f.unread(c);
					columna--;
					estado = 0;
					break;
				case 29:
					token = new Token (cad, "++", linea, columna-cad.length());
					//guardo mas mas
					lista.add(token);
					estado = 0;
					break;
				case 30:
					token = new Token (cad, "oper_arit", linea, columna-cad.length());
					//guardo menos, multiplicar y dividir
					lista.add(token);
					estado = 0;
					break;
				case 33:
					c = leerCaracter ();
					if (c == '#' || c=='$') {
						error = new Error (linea, columna, "Operador ! no existe");
						errores.insertarError(error, ConjuntoErrores.TipoError.LEXICO);
						return;
					}
					if (c == '=') {
						cad = cad + c;
						estado = 34;
					}
					else
						estado = 38;
					break;
				case 34:
					token = new Token (cad, "oper_logico", linea, columna-cad.length());
					//guardo distinto
					lista.add(token);
					estado = 0;
					break;
				case 37:
					//error, tengo dos puntos solos
					error = new Error (linea, columna, "Operador : no existe");
					errores.insertarError(error, ConjuntoErrores.TipoError.LEXICO);
					f.unread(c);
					columna--;
					estado = 0;
					break;
				case 38:
					//error, tengo una exclamacion sola
					error = new Error (linea, columna, "Operador ! no existe");
					errores.insertarError(error, ConjuntoErrores.TipoError.LEXICO);
					f.unread(c);
					columna--;
					estado = 0;
					break;
				}//switch
			}//while
		}
		catch (Exception e) {
			
		}
	}
}
